//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#include "TextBox.h"

#include "Clipboard.h"


namespace D2 {

TextBox::TextBox(int x, int y, int width, int height) : DisplayObject(x, y, width, height) {
	selectable = true;

	_pointerCounter = 0;

	_textIndexIStart = 0; //current char index from the text where the pointer will be shown.
    _textIndexIEnd = 0;

	_textIndexJStart = 0; //current char index from the text where the pointer will be shown.
    _textIndexJEnd = 0;

	_maxInputStop = false;

	_text = "";
	_rows = 1;

	_writeable = true;

	_fg.r = 0.8f;
	_fg.g = 0.8f;
	_fg.b = 0.8f;

	_bg.r = 0.2f;
	_bg.g = 0.2f;
	_bg.b = 0.2f;
	//_bg._a = 0.0f;

	_currentMode = TextBox::TextMode::CONSOLE;

	_charWidth = 0;
	_charHeight = 0;

	_totalWidth = 0;
	_totalHeight = 0;

	_maxRows = 0;

	showBackground = true;

	_blockOnPrevRow = true; //defines if it is possible to remove the \r (return).
	_clearFirstOnReachedEnd = true;

#ifdef _DEBUG
	name = "D2::TextBox";
#endif
}

TextBox::~TextBox(void) {

}

void TextBox::SetMode(TextBox::TextMode mode) {
	_currentMode = mode;
}

/////////////////////////////DATA////////////////////////////
bool TextBox::Draw(Direct2DContext* ctx, D2D1::Matrix3x2F* mtx) {
	//ctx->_disableCamera = true;

//meassure text
	Direct2DContext::Dimension dim = ctx->measureText(_text);

	//meassure text.
	_charWidth = dim.width/_text.length()*1.0f; //caluclate char width;
	_charHeight = dim.height/_rows; //caluclate char height;
	

	bool totalWidthChanged = false;
	bool totalHeightChanged = false;
	if(dim.width != _totalWidth) {
		totalWidthChanged = true;
	}
	if(dim.height != _totalHeight) {
		totalHeightChanged = true;
	}

	_totalWidth = dim.width;
	_totalHeight = dim.height;
	_maxRows = _height/dim.height; //calculate max rows.

//
	ctx->lineWidth = 1;

	if(showBackground) {
		ctx->SetFillStyle(_bg.r, _bg.g, _bg.b);
		ctx->fillRect(0, 0, (float)this->_width, (float)this->_height);
	}
//draw the text
	ctx->SetStrokeStyle(_fg.r, _fg.g, _fg.b);
	ctx->fillText(_text, 0, 0, 20);

//draw text pointer.
	/*
	int positionX = _charWidth * _textIndexIEnd;
	int positionY = _rows * dim.height;

	ctx->lineWidth = 3;
    ctx->SetStrokeStyle(1.0f, 0.0f, 0.0f);

    ctx->beginPath();

	ctx->moveTo(positionX, positionY);
    ctx->lineTo(positionX, positionY + dim.height);

    ctx->stroke();
	*/
//

//TEST
	/*
	ctx->SetStrokeStyle(1, 0, 1);
	ctx->fillText("_textIndexIStart: " + Parse::to_string<int>(_textIndexIStart), 0, 0, 20);
	ctx->fillText("_textIndexIEnd: " + Parse::to_string<int>(_textIndexIEnd), 0, 20*1, 20);
	ctx->fillText("_textIndexJStart: " + Parse::to_string<int>(_textIndexJStart), 0, 20*2, 20);
	ctx->fillText("_textIndexJEnd: " + Parse::to_string<int>(_textIndexJEnd), 0, 20*3, 20);
	ctx->fillText("_charWidth: " + Parse::to_string<float>(_charWidth), 0, 20*4, 20);
	ctx->fillText("_charHeight: " + Parse::to_string<int>(_charHeight), 0, 20*5, 20);
	ctx->fillText("_totalWidth: " + Parse::to_string<float>(_totalWidth), 0, 20*6, 20);
	ctx->fillText("_totalHeight: " + Parse::to_string<int>(_totalHeight), 0, 20*7, 20);
	ctx->fillText("_rows: " + Parse::to_string<int>(_rows), 0, 20*8, 20);
	*/
//TEST


	//ctx->_disableCamera = false;

	if(totalWidthChanged) {
		//throw event.
		this->dispatchEvent(Event("TOTAL_WIDTH_CHANGED", this));
	}

	if(totalHeightChanged) {
		//throw event.
		this->dispatchEvent(Event("TOTAL_HEIGHT_CHANGED", this));
	}

	return true;
}

void TextBox::OnKeyUp(const KeyboardEvent &e) {
	
}

void TextBox::OnKeyDown(const KeyboardEvent &e) {
	if(!_writeable) return;
	if(!selectable) return;

	/*
	int key = e._keyCode;

	if(key == DIK_RETURN) { //new line
		this->dispatchEvent(Event("RETURN", this));
		_rows++;

		handleOutOfRows();
	}

	if(key == DIK_BACKSPACE) { //delete
		int length = _text.length()-1;
		if(length >= 0) {
			//cut one char.
			if(_textIndexIEnd == _textIndexIStart) {
				if(_blockOnPrevRow) {
					char curr_char = _text[_text.length()-1];
					if(curr_char == '\r' || curr_char == '\n') {
						return;
					}
				}

				if(_textIndexIEnd > 0) {
					//1234|56789 -> 123|56789 //cut the area out and replace it with the new char.
					_text = _text.substr(0, _textIndexIEnd-1) + _text.substr(_textIndexIEnd, _text.length()); //slice

                    _textIndexIEnd--;
                    _textIndexIStart--;
                 }
			}

			//cut area of chars.
            //determine which are the min and which are the max index.
            int minIndex = min(_textIndexIStart, _textIndexIEnd);
            int maxIndex = max(_textIndexIStart, _textIndexIEnd);

            //123[456]789 -> 123A789  , [ = minIndex, ] = maxIndex //cut the area out and replace it with the new char.
            _text = _text.substr(0, minIndex) + _text.substr(maxIndex, _text.length());

            //raise the text indexes.
            _textIndexIEnd = minIndex;
            _textIndexIStart = minIndex;

            //var evt2 = new Event("CHANGE", this, event); //Event.CHANGE
            //this.dispatchEvent(evt2);
		}

        return;
	}
	*/

	if(e._strgKey && e._keyCode == 46) { //strg + c (copy)
		Clipboard::CopyToClipboard(_text);
		return;
	}

	if(e._strgKey && e._keyCode == 47) { //strg + v (paste)
		_text += Clipboard::ClipboardToString();
		return;
	}

//
	char charCode = e._charCode; //e._charCode;
	std::string charCode_str = "";
	charCode_str.insert(0, 1, charCode);

	if(charCode == '\0') return;

    //determine the min and the max index.
    int minIndex = min(_textIndexIStart, _textIndexIEnd);
    int maxIndex = max(_textIndexIStart, _textIndexIEnd);

    //cut the area out and replace it with the new char.  123[456]789 -> 123A789  , [ = minIndex, ] = maxIndex
    _text = _text.substr(0, minIndex) + charCode_str + _text.substr(maxIndex, _text.length());

    //prevent for override the textField with inputs.
    if(_maxInputStop) {
		/*
		int diff = _width - (_text.x + _text.getMeasuredWidth() + 5);
        if(diff < 0) {
			_text = oldText;
        } else {
			//raise the text indexes.
			_textIndexEnd = minIndex+1;
               _textIndexStart = minIndex+1;
        }
		*/
	} else {
		//raise the text indexes.
		_textIndexIEnd = minIndex+1;
        _textIndexIStart = minIndex+1;
    }

	return;
}

void TextBox::OnMouseClick(const MouseEvent &e) {
	int a = 0;
	//if(!selectable) return;

	//_pointerCounter = 0;

	//D2D1_POINT_2F point = this->globalToLocal(e._stageX, e._stageY);

}

void TextBox::OnMouseUp(const MouseEvent &e) {
	int a = 0;
}

void TextBox::OnMouseDown0(const MouseEvent &e) {
	/*
	//if(!selectable) return;

	D2D1_POINT_2F point = this->globalToLocal(e._stageX, e._stageY);
        
	_textIndexIStart = GetCharIndex(point.x);
    _textIndexIEnd = _textIndexIStart;
	*/
}

/* (picking)
* returns the index where is the position on the text.
*/
int TextBox::GetCharIndex(float x) {
	//calculate the index where is picking.
    //float localSpaceX = x - 0; //_text.x;

	int index = floor(x/_charWidth)+1;

	//clamp the index.
	if(index > _text.length()) index = _text.length();
	if(index < 0) index = 0;

	return index;
}

/*
* checks the rows and cuts the rows that are higher than maxRows.
*/
void TextBox::handleOutOfRows_console() {
	if(_clearFirstOnReachedEnd) {
		if(_rows > (_maxRows-1)) {
			int cutRows = _rows - (_maxRows-1); //amount of rows that have to be removed.
			int returnCounter = 0;
			
			for(int i = 0; i < _text.length(); i++) {
				char c = _text[i];
				if(c == '\r' || c == '\n') {
					returnCounter++;
				}
				if(returnCounter == cutRows) {
					_rows -= cutRows;
					_text = _text.substr(i+1, _text.length()-1);
					setTextPointerToEnd();

					break;
				}
			}			
		}
	}
}

/*
* checks the rows and cuts the rows that are higher than maxRows.
*/
void TextBox::handleOutOfRows_scroll() {
	
}

/*
* returns the whole text.
*/
std::string TextBox::GetText() {
	return _text;
}

/*
* returns the text until the last return symbol
*/
std::string TextBox::GetText_lr() {
	std::string text = "";

	int length = _text.length();

	for(int i = length-1; i >= 0; i--) {
		char c = _text[i];
		
		if(c == '\r' || c == '\n') {
			return text;
		} else {
			text = c + text;
		}
	}
	
	return text;
}

void TextBox::clear_lr() {
	int length = _text.length();

	int matchedIndex = 0;
	for(int i = length-1; i >= 0; i--) {
		char c = _text[i];

		if(c == '\r' || c == '\n') {
			matchedIndex = i;
			break;
		}
	}
	
	_text = _text.substr(0, matchedIndex+1);
}

/*
* appends a text to the exist text.
*/
void TextBox::SetText(std::string text) {
	_text = _text;
	setTextPointerToEnd();

	_rows = std::count(_text.begin(), _text.end(), '\n');
	_rows = std::count(_text.begin(), _text.end(), '\r');

	handleOutOfRows();
}

/*
* appends a text to the exist text.
*/
void TextBox::AppendText(std::string text) {
	_text += text;
	setTextPointerToEnd();

	_rows += std::count(text.begin(), text.end(), '\n');
	_rows += std::count(text.begin(), text.end(), '\r');

	handleOutOfRows();
}

void TextBox::handleOutOfRows() {
	switch(_currentMode) {
		case TextBox::TextMode::NONE:
			break;
		case TextBox::TextMode::CONSOLE:
			handleOutOfRows_console();
			break;
		case TextBox::TextMode::SCROLL:
			handleOutOfRows_scroll();
			break;
		default:
			break;
	}
}

/*
* moves the text pointer to a different position.
*/
void TextBox::setTextPointer(int index) {
	if(index < 0 || index > _text.size()) return;

	_textIndexIStart = index; //current char index from the text where the pointer will be shown.
    _textIndexIEnd = index;
}

/*
* moves the text pointer to the end of the text.
*/
void TextBox::setTextPointerToEnd() {
	setTextPointer(_text.size());
}

int TextBox::GetRows() {
	return _rows+1;
}

void TextBox::SetFgColor(float r, float g, float b) {
	_fg.r = r;
	_fg.g = g;
	_fg.b = b;
}

void TextBox::SetBgColor(float r, float g, float b) {
	_bg.r = r;
	_bg.g = g;
	_bg.b = b;
}

float TextBox::GetTotalWidth() {
	return _totalWidth;
}

float TextBox::GetTotalHeight() {
	return _totalHeight;
}

}